"""
插件管理器
支持ZIP文件上传、解压和动态蓝图管理
"""
import os
import zipfile
import tempfile
import shutil
import json
import importlib
import sys
from typing import Dict, List, Optional
from flask import Flask, Blueprint
from werkzeug.utils import secure_filename
import logging
import datetime

logger = logging.getLogger(__name__)


class PluginManager:
    """插件管理器"""
    
    def __init__(self, app: Flask):
        """初始化插件管理器"""
        self.app = app
        self.plugin_dir = os.path.join(app.root_path, '..', 'plugins')
        self.plugins_file = os.path.join(self.plugin_dir, 'plugins.json')
        self.plugins = {}
        
        # 确保插件目录存在
        os.makedirs(self.plugin_dir, exist_ok=True)
        
        # 加载已安装的插件信息
        self._load_plugins_info()
        
        # 在应用启动时注册所有已安装的插件
        self._register_installed_plugins()
    
    def _load_plugins_info(self):
        """加载已安装的插件信息"""
        info_file = os.path.join(self.plugin_dir, 'plugins.json')
        if os.path.exists(info_file):
            try:
                with open(info_file, 'r', encoding='utf-8') as f:
                    self.plugins = json.load(f)
            except Exception as e:
                logger.error(f"加载插件信息失败: {e}")
                self.plugins = {}
    
    def _save_plugins_info(self):
        """保存插件信息"""
        info_file = os.path.join(self.plugin_dir, 'plugins.json')
        try:
            with open(info_file, 'w', encoding='utf-8') as f:
                json.dump(self.plugins, f, ensure_ascii=False, indent=2)
        except Exception as e:
            logger.error(f"保存插件信息失败: {e}")
    
    def upload_plugin(self, zip_file, plugin_name: str = None) -> Dict:
        """上传并安装插件"""
        try:
            # 获取安全的文件名
            filename = secure_filename(zip_file.filename)
            if not filename.endswith('.zip'):
                return {'success': False, 'msg': '只支持ZIP格式的插件文件'}
            
            # 确定插件名称
            if plugin_name is None:
                plugin_name = filename[:-4]
            
            # 创建临时目录
            with tempfile.TemporaryDirectory() as temp_dir:
                # 保存ZIP文件
                zip_path = os.path.join(temp_dir, filename)
                zip_file.save(zip_path)
                
                # 解压ZIP文件
                plugin_dir = os.path.join(temp_dir, plugin_name)
                with zipfile.ZipFile(zip_path, 'r') as zip_ref:
                    zip_ref.extractall(plugin_dir)
                
                # 验证插件结构
                validation_result = self._validate_plugin(plugin_dir, plugin_name)
                if not validation_result['success']:
                    return validation_result
                
                # 安装插件
                return self._install_plugin(plugin_dir, plugin_name)
                
        except Exception as e:
            logger.error(f"上传插件失败: {e}")
            return {'success': False, 'msg': f'上传插件失败: {str(e)}'}
    
    def _validate_plugin(self, plugin_dir: str, plugin_name: str) -> Dict:
        """验证插件结构"""
        try:
            # 检查必需文件
            required_files = ['plugin.json', '__init__.py']
            for file in required_files:
                file_path = os.path.join(plugin_dir, file)
                if not os.path.exists(file_path):
                    return {'success': False, 'msg': f'插件缺少必需文件: {file}'}
            
            # 读取插件配置
            config_path = os.path.join(plugin_dir, 'plugin.json')
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
            
            # 验证配置
            required_fields = ['name', 'version', 'description', 'author', 'blueprint_name']
            for field in required_fields:
                if field not in config:
                    return {'success': False, 'msg': f'插件配置缺少必需字段: {field}'}
            
            # 检查蓝图文件
            blueprint_name = config.get('blueprint_name')
            blueprint_file = os.path.join(plugin_dir, f'{blueprint_name}.py')
            if not os.path.exists(blueprint_file):
                return {'success': False, 'msg': f'插件缺少蓝图文件: {blueprint_name}.py'}
            
            return {'success': True, 'config': config}
            
        except Exception as e:
            return {'success': False, 'msg': f'验证插件失败: {str(e)}'}
    
    def _install_plugin(self, plugin_dir: str, plugin_name: str) -> Dict:
        """安装插件"""
        try:
            # 读取插件配置
            config_path = os.path.join(plugin_dir, 'plugin.json')
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
            
            # 检查插件是否已安装
            if plugin_name in self.plugins:
                return {'success': False, 'msg': f'插件 {plugin_name} 已经安装'}
            
            # 复制插件文件到插件目录
            target_dir = os.path.join(self.plugin_dir, plugin_name)
            if os.path.exists(target_dir):
                shutil.rmtree(target_dir)
            shutil.copytree(plugin_dir, target_dir)
            
            # 注册蓝图
            blueprint_name = config.get('blueprint_name')
            module_path = f'plugins.{plugin_name}.{blueprint_name}'
            
            # 动态导入模块
            sys.path.insert(0, self.plugin_dir)
            try:
                logger.info(f"开始导入模块: {plugin_name}.{blueprint_name}")
                module = importlib.import_module(f'{plugin_name}.{blueprint_name}')
                logger.info(f"模块导入成功: {module}")
                
                # 查找蓝图实例
                blueprint = None
                logger.info(f"模块属性: {dir(module)}")
                for attr_name in dir(module):
                    attr = getattr(module, attr_name)
                    logger.info(f"检查属性: {attr_name} -> {type(attr)}")
                    if isinstance(attr, Blueprint):
                        blueprint = attr
                        logger.info(f"找到蓝图实例: {blueprint}")
                        break
                
                if not blueprint:
                    logger.error(f"在模块 {module_path} 中未找到蓝图实例")
                    return {'success': False, 'msg': f'在模块 {module_path} 中未找到蓝图实例'}
                
                logger.info(f"蓝图信息:")
                logger.info(f"  名称: {blueprint.name}")
                logger.info(f"  URL前缀: {blueprint.url_prefix}")
                logger.info(f"  视图函数: {list(blueprint.view_functions.keys())}")
                logger.info(f"  延迟函数数量: {len(blueprint.deferred_functions)}")
                
                # 使用动态蓝图注册方法
                logger.info(f"开始注册蓝图: {blueprint.name}")
                success = self._register_blueprint_dynamic(blueprint, module_path, config)
                if not success:
                    logger.error(f"注册蓝图失败: {blueprint.name}")
                    return {'success': False, 'msg': f'注册蓝图失败'}
                
                logger.info(f"蓝图注册成功: {blueprint.name}")
                
                # 保存插件信息
                self.plugins[plugin_name] = {
                    'name': config.get('name'),
                    'version': config.get('version'),
                    'description': config.get('description'),
                    'author': config.get('author'),
                    'blueprint_name': blueprint_name,
                    'module_path': module_path,
                    'url_prefix': config.get('url_prefix', f'/{plugin_name}'),
                    'installed_at': str(datetime.datetime.now())
                }
                self._save_plugins_info()
                
                # 立即注册蓝图到应用
                try:
                    # 使用动态注册方法，而不是直接调用register_blueprint
                    success = self._register_blueprint_dynamic(blueprint, module_path, config)
                    if success:
                        logger.info(f"✓ 插件 {plugin_name} 蓝图已注册到应用")
                    else:
                        logger.warning(f"⚠ 插件 {plugin_name} 蓝图注册到应用失败")
                except Exception as e:
                    logger.warning(f"⚠ 插件 {plugin_name} 蓝图注册到应用失败: {e}")
                
                logger.info(f"插件 {plugin_name} 安装成功")
                return {
                    'success': True,
                    'msg': f'插件 {plugin_name} 安装成功',
                    'plugin': self.plugins[plugin_name]
                }
                
            finally:
                # 恢复sys.path
                if self.plugin_dir in sys.path:
                    sys.path.remove(self.plugin_dir)
            
        except Exception as e:
            logger.error(f"安装插件失败: {e}")
            return {'success': False, 'msg': f'安装插件失败: {str(e)}'}
    
    def _register_blueprint_dynamic(self, blueprint: Blueprint, module_path: str, config: Dict) -> bool:
        """动态注册蓝图"""
        try:
            logger.info(f"开始动态注册蓝图: {blueprint.name}")
            
            # 检查应用是否已经处理过请求
            has_first_request = hasattr(self.app, '_got_first_request') and self.app._got_first_request
            logger.info(f"应用是否已处理第一个请求: {has_first_request}")
            
            if has_first_request:
                # 如果应用已经初始化，使用手动注册方法
                logger.info(f"应用已初始化，使用手动注册方法")
                return self._register_blueprint_manual(blueprint, config)
            else:
                # 应用还未初始化，可以直接注册
                logger.info(f"应用未初始化，使用标准注册方法")
                self.app.register_blueprint(blueprint)
                logger.info(f"蓝图注册成功: {blueprint.name}")
                return True
                
        except Exception as e:
            logger.error(f"注册蓝图失败: {e}")
            import traceback
            logger.error(traceback.format_exc())
            return False
    
    def _register_blueprint_manual(self, blueprint: Blueprint, config: Dict) -> bool:
        """手动注册蓝图（应用已初始化时）"""
        try:
            blueprint_name = blueprint.name
            url_prefix = blueprint.url_prefix or ''
            
            logger.info(f"手动注册蓝图: {blueprint_name}, URL前缀: {url_prefix}")
            logger.info(f"蓝图视图函数: {list(blueprint.view_functions.keys())}")
            logger.info(f"蓝图延迟函数数量: {len(blueprint.deferred_functions)}")
            
            # 注册视图函数
            for endpoint, view_func in blueprint.view_functions.items():
                full_endpoint = f"{blueprint_name}.{endpoint}"
                self.app.view_functions[full_endpoint] = view_func
                logger.info(f"注册视图函数: {full_endpoint}")
            
            # 方法1: 尝试直接注册（可能会失败，但值得一试）
            try:
                self.app.register_blueprint(blueprint)
                logger.info(f"✓ 蓝图 {blueprint_name} 直接注册成功")
                return True
            except Exception as e:
                logger.warning(f"直接注册失败: {e}")
                # 继续执行手动注册方法
                logger.info(f"使用手动路由注册方法")
            
            # 为每个视图函数创建路由
            for endpoint, view_func in blueprint.view_functions.items():
                full_endpoint = f"{blueprint_name}.{endpoint}"
                
                # 构建URL规则
                if endpoint == 'index':
                    rule = url_prefix + '/'
                else:
                    rule = url_prefix + f'/{endpoint}'
                
                from werkzeug.routing import Rule
                new_rule = Rule(
                    rule,
                    endpoint=full_endpoint,
                    methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD']
                )
                
                self.app.url_map.add(new_rule)
                logger.info(f"注册路由: {rule} -> {full_endpoint}")
            
            # 验证注册结果
            logger.info(f"注册后的路由:")
            for rule in self.app.url_map.iter_rules():
                if rule.endpoint.startswith(f'{blueprint_name}.'):
                    logger.info(f"  {rule.rule} -> {rule.endpoint}")
            
            logger.info(f"✓ 蓝图 {blueprint_name} 手动注册成功")
            return True
            
        except Exception as e:
            logger.error(f"手动注册蓝图失败: {e}")
            import traceback
            logger.error(traceback.format_exc())
            return False
    
    def uninstall_plugin(self, plugin_name: str) -> Dict:
        """卸载插件"""
        try:
            # 检查插件是否已安装
            if plugin_name not in self.plugins:
                return {'success': False, 'msg': f'插件 {plugin_name} 未安装'}
            
            plugin_info = self.plugins[plugin_name]
            blueprint_name = plugin_info.get('blueprint_name')
            
            # 卸载蓝图
            try:
                # 从应用中移除蓝图
                for rule in list(self.app.url_map.iter_rules()):
                    if rule.endpoint.startswith(f'{blueprint_name}.'):
                        self.app.url_map._rules.remove(rule)
                        if rule.endpoint in self.app.url_map._rules_by_endpoint:
                            del self.app.url_map._rules_by_endpoint[rule.endpoint]
                
                # 移除视图函数
                for endpoint in list(self.app.view_functions.keys()):
                    if endpoint.startswith(f'{blueprint_name}.'):
                        del self.app.view_functions[endpoint]
                
                logger.info(f"蓝图 {blueprint_name} 卸载成功")
            except Exception as e:
                logger.warning(f"卸载蓝图时出错: {e}")
            
            # 删除插件文件
            plugin_dir = os.path.join(self.plugin_dir, plugin_name)
            if os.path.exists(plugin_dir):
                shutil.rmtree(plugin_dir)
            
            # 从插件信息中移除
            del self.plugins[plugin_name]
            self._save_plugins_info()
            
            logger.info(f"插件 {plugin_name} 卸载成功")
            return {'success': True, 'msg': f'插件 {plugin_name} 卸载成功'}
            
        except Exception as e:
            logger.error(f"卸载插件失败: {e}")
            return {'success': False, 'msg': f'卸载插件失败: {str(e)}'}
    
    def list_plugins(self) -> List[Dict]:
        """获取已安装的插件列表"""
        return list(self.plugins.values())
    
    def get_plugin_info(self, plugin_name: str) -> Optional[Dict]:
        """获取插件信息"""
        return self.plugins.get(plugin_name)
    
    def is_plugin_installed(self, plugin_name: str) -> bool:
        """检查插件是否已安装"""
        return plugin_name in self.plugins

    def _register_installed_plugins(self):
        """注册所有已安装的插件"""
        logger.info("开始注册已安装的插件...")
        
        for plugin_name, plugin_info in self.plugins.items():
            try:
                logger.info(f"注册插件: {plugin_name}")
                
                # 检查插件目录是否存在
                plugin_path = os.path.join(self.plugin_dir, plugin_name)
                if not os.path.exists(plugin_path):
                    logger.warning(f"插件目录不存在: {plugin_path}")
                    continue
                
                # 导入插件模块
                sys.path.insert(0, self.plugin_dir)
                try:
                    module = importlib.import_module(f'{plugin_name}.{plugin_info["blueprint_name"]}')
                    
                    # 查找蓝图实例
                    blueprint = None
                    for attr_name in dir(module):
                        attr = getattr(module, attr_name)
                        if isinstance(attr, Blueprint):
                            blueprint = attr
                            break
                    
                    if blueprint:
                        # 注册蓝图
                        self.app.register_blueprint(blueprint)
                        logger.info(f"✓ 插件 {plugin_name} 注册成功")
                    else:
                        logger.error(f"✗ 插件 {plugin_name} 中未找到蓝图")
                        
                except Exception as e:
                    logger.error(f"✗ 注册插件 {plugin_name} 失败: {e}")
                    
                finally:
                    if self.plugin_dir in sys.path:
                        sys.path.remove(self.plugin_dir)
                        
            except Exception as e:
                logger.error(f"✗ 处理插件 {plugin_name} 时出错: {e}")
        
        logger.info("插件注册完成")


# 全局插件管理器实例
_plugin_manager = None


def get_plugin_manager(app: Flask = None) -> PluginManager:
    """获取插件管理器实例"""
    global _plugin_manager
    
    if app is None:
        from flask import current_app
        app = current_app
    
    if _plugin_manager is None or _plugin_manager.app != app:
        _plugin_manager = PluginManager(app)
    
    return _plugin_manager


def upload_plugin_dynamic(zip_file, plugin_name: str = None) -> Dict:
    """动态上传插件"""
    manager = get_plugin_manager()
    return manager.upload_plugin(zip_file, plugin_name)


def uninstall_plugin_dynamic(plugin_name: str) -> Dict:
    """动态卸载插件"""
    manager = get_plugin_manager()
    return manager.uninstall_plugin(plugin_name)


def list_plugins_dynamic() -> List[Dict]:
    """获取已安装的插件列表"""
    manager = get_plugin_manager()
    return manager.list_plugins()


def get_plugin_info_dynamic(plugin_name: str) -> Optional[Dict]:
    """获取插件信息"""
    manager = get_plugin_manager()
    return manager.get_plugin_info(plugin_name) 